package com.example.point;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;

public class MyRenderer implements GLSurfaceView.Renderer{

	public MyRenderer(){
		/*
		 * this vertBuffer is a float buffer
		 * points to note we allocate space for vertData.length * 4
		 * this is because in our vertData array we have 3 elements 1.0f, 1.0f, 0.0f thats for the color yellow
		 * now each float has a byte size of 4 so we multiply the vertData length * 4 to get the total memory size
		 * we then tell the bytebuffer to have a native byte order, and we tell it that it's a float buffer
		 * then we put the data in the array at position zero.
		 */
		vertBuffer = ByteBuffer.allocateDirect(vertData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
		vertBuffer.put(vertData).position(0);
	}
	
	@Override
	public void onDrawFrame(GL10 gl) {
		/*
		 * here we clear the color buffer bit, think erasing a board after you draw once
		 * so that you dont have left over junk from previous draw
		 */
		GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
		/*
		 * we then set the program to use to draw; this is the program we compiled
		 */
		GLES20.glUseProgram(sProgram);
		
		/*
		 * Here are some matrix stuff, we load our model matrix
		 * then we translate (think push) the model back 4 units
		 */
		Matrix.setIdentityM(modelMatrix, 0);
		Matrix.translateM(modelMatrix, 0, 0.0f, 0.0f, -4.0f);
		
		/*
		 * then we tell the vertBuffer to start at position zero
		 * then we tell opengl, we start at position 0 and go to position 3 to get the vertex data
		 * gles20.gl_float tells opengl that we are using float data
		 * just use false for the next value, then 0
		 * then we tell openGL that we will be using the buffer we created earlier vertBuffer
		 * we then enable the position handle to tell the shader to pass the vertex positions into the shader
		 */
		
		vertBuffer.position(0);
		GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false, 0, vertBuffer);
		GLES20.glEnableVertexAttribArray(posHandle);
		
		/*
		 * here we do some matrix multiplication
		 * we multiply the model matrix by the view matrix and save it in the model view matrix
		 * then we multiply the model view projection matrix by the projection matrix and again
		 * save that in the model view projection matrix
		 */
		
		Matrix.multiplyMM(modViewProjMatrix, 0, viewMatrix, 0, modelMatrix, 0);
		Matrix.multiplyMM(modViewProjMatrix, 0, projMatrix, 0, modViewProjMatrix, 0);
		
		/*
		 * then we pass in this model view projection matrix into our shader
		 */
		
		GLES20.glUniformMatrix4fv(MVPMatHandle, 1, false, modViewProjMatrix, 0);
		
		/*
		 * here we actually tell opengl to draw,
		 * we will be droing points so we use gles20.gl_points, we start at position zero and draw 1 point
		 */
		
		GLES20.glDrawArrays(GLES20.GL_POINTS, 0, 1);
		
		
	}

	@Override
	public void onSurfaceChanged(GL10 gl, int width, int height) {
		mWidth = width;
		mHeight = height;
		//set the viewport to the width and height of the screen
		GLES20.glViewport(0, 0, mWidth, mHeight);
		
		/*
		 * setting up the frustum
		 */
		final float ratio = (float)mWidth/mHeight;
		final float left = -ratio;
		final float right = ratio;
		final float bottom = -1.0f;
		final float top = 1.0f;
		final float near = 3.0f;
		final float far = 10.0f;
		
		Matrix.frustumM(projMatrix, 0, left, right, bottom, top, near, far);
	}

	@Override
	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		//Clear our board color, clear it to black
		GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
		
		/*
		 * this matrix is your camera position
		 */
		
		final float eyeX = 0.0f;
		final float eyeY = 0.0f;
		final float eyeZ = 3.0f;
		final float centerX = 0.0f;
		final float centerY = 0.0f;
		final float centerZ = 0.0f;
		final float upX = 0.0f;
		final float upY = 1.0f;
		final float upZ = 0.0f;
		
		Matrix.setLookAtM(viewMatrix, 0, eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
		
		/*
		 * here we create our shaders and link them to the shader program
		 */
		
		vShader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
		GLES20.glShaderSource(vShader, vCode);
		GLES20.glCompileShader(vShader);
		
		fShader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
		GLES20.glShaderSource(fShader, fCode);
		GLES20.glCompileShader(fShader);
		
		sProgram = GLES20.glCreateProgram();
		GLES20.glAttachShader(sProgram, vShader);
		GLES20.glAttachShader(sProgram, fShader);
		GLES20.glLinkProgram(sProgram);
		
		/*
		 * here we grab the position attribute from the vertex shader
		 * we also grab the model view projection matrix handle from the shader
		 * **note**
		 * the position handle is an attribute so get use gles20.glgetattributelocation(program, the name in the shader)
		 * the mod view proj handle is an uniform so we use gles20.glgetuniformlocation(program, the name in the shader)
		 */
		
		posHandle = GLES20.glGetAttribLocation(sProgram, "aPos");
		MVPMatHandle = GLES20.glGetUniformLocation(sProgram, "uMVPMat");
	}

	/*
	 * below are all the variables we will need to get this demo code up
	 * we have model, view, projection, and model view projection matrices
	 * we have a float buffer to store our vertex data in a order that openGL understands
	 * we have handles for our vertex and fragment shaders, we also have a handle for the shader program which we will use
	 * 
	 * next we have handles to pass in data into our shaders;
	 * posHandle - passes in vertex positions to the shader
	 * MVPMatHandle - passes in the model view projection matrix to transform our positions to proper screen space
	 * next we have a local width and height variables
	 * 
	 * we then define our vertex shader
	 * then the fragment shader
	 * then the vertex data
	 */
	
	private float[] modelMatrix = new float[16];
	private float[] viewMatrix = new float[16];
	private float[] projMatrix = new float[16];
	private float[] modViewProjMatrix = new float[16];
	private FloatBuffer vertBuffer;
	private int vShader, fShader, sProgram, posHandle, MVPMatHandle, mWidth, mHeight;
	
	/*
	 * this is a very simple vertex shader
	 * there is a uniform matrix4 called uMVPMat it is the combined model view projection matrix
	 * we have an attribute aPos we will use our posHandle to pass in this value
	 * here since we are working with only one point we change the size to a very large size 450.0
	 * then we set the vertex position by multiplying the combined matrix by the vertex position
	 */
	
	private final String vCode =
			 "uniform mat4   uMVPMat;        \n"
			+"attribute vec4 aPos;           \n"
			+"void main(){                   \n"
			+" gl_PointSize = 450.0;         \n"
			+" gl_Position = uMVPMat * aPos; \n"
			+"}                              \n";
	
	/*
	 * this fragmentshader first thing we need to do is define the precision of the shader
	 * for mobile I think mediump is OK
	 * now all we do is tell the vertex shader to color all the fragments (think pixels) yellow;
	 */
	private final String fCode =
			 "precision mediump float;                  \n"
			+"void main(){                              \n"
			+" gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0); \n"
			+"}                                         \n";
	
	/*
	 * the vertex data for this app is very simple, 
	 * its just a point in the center of the screen
	 */
	private final float[] vertData = { 
			0.0f, 0.0f, 0.0f 
			};
}
